﻿using System;
using System.Collections;
using System.IO;
using System.Text;
using System.Data;

namespace SKN
{
    public class CSVReader
    {
        private StreamReader oReader;
        private bool _hasHeaderRow = false;
        private int _totalRecordsLoaded = 0;
        private int _maxRecordsToLoad = -1;

        public CSVReader (Stream stream) : this(stream, null) { }

        public CSVReader (Stream stream, Encoding enc)
        {
            //check the Pass Stream whether it is readable or not
            if (!stream.CanRead)
            {
                return;
            }
            if (enc != null)
            {
                oReader = new StreamReader(stream, enc);
            }
            else
            {
                oReader = new StreamReader(stream, true);
            }
        }

        public CSVReader (string filePath)
        {
            oReader = new StreamReader(filePath);
        }

        public DataTable GetDataTable()
        {
            DataTable csvDataTable = new DataTable();
            string[] rowData = GetCSVLine();

            if (rowData != null)
            {                
                if (_hasHeaderRow)
                {
                    // first row contains column headings - add these columns to the data table
                    foreach (string header in rowData)
                    {
                        csvDataTable.Columns.Add(header);
                    }
                }
                else
                {
                    // first row contains actual data, 
                    // so first add columns with name=index to the data table, 
                    // then add the data row
                    for (int i = 0; i < rowData.Length; i++)
                    {
                        csvDataTable.Columns.Add(i.ToString());
                    }

                    csvDataTable.Rows.Add(rowData);
                    _totalRecordsLoaded++;
                }
            }
            else
            {
                throw new ApplicationException("Import file contains no data.");
            }

            // add the (rest of the) data
            while ((rowData = GetCSVLine()) != null &&
                (_maxRecordsToLoad <= 0 || _totalRecordsLoaded < _maxRecordsToLoad))
            {
                csvDataTable.Rows.Add(rowData);
                _totalRecordsLoaded++;
            }

            return csvDataTable;
        }

        private string[] GetCSVLine ()
        {
            string data = oReader.ReadLine();
            if (data == null) return null;
            if (data.Length == 0) return new string[0];
            ArrayList result = new ArrayList();
            ParseCSVData(result, data);
            return (string[])result.ToArray(typeof(string));
        }

        private void ParseCSVData (ArrayList result, string data)
        {
            int position = -1;
            while (position < data.Length)
                result.Add(ParseCSVField(ref data, ref position));
        }

        private string ParseCSVField (ref string data, ref int StartSeperatorPos)
        {
            if (StartSeperatorPos == data.Length - 1)
            {
                StartSeperatorPos++;
                return "";
            }

            int fromPos = StartSeperatorPos + 1;
            if (data[fromPos] == '"')
            {
                int nextSingleQuote = GetSingleQuote(data, fromPos + 1);
                int lines = 1;
                while (nextSingleQuote == -1)
                {
                    data = data + "\n" + oReader.ReadLine();
                    nextSingleQuote = GetSingleQuote(data, fromPos + 1);
                    lines++;
                    if (lines > 20)
                        throw new Exception("lines overflow: " + data);
                }
                StartSeperatorPos = nextSingleQuote + 1;
                string tempString = data.Substring(fromPos + 1, nextSingleQuote - fromPos - 1);
                // tempString = tempString.Replace("'", "''");
                return tempString.Replace("\"\"", "\"");
            }

            int nextComma = data.IndexOf(',', fromPos);
            if (nextComma == -1)
            {
                StartSeperatorPos = data.Length;
                return data.Substring(fromPos);
            }
            else
            {
                StartSeperatorPos = nextComma;
                return data.Substring(fromPos, nextComma - fromPos);
            }
        }

        private int GetSingleQuote (string data, int SFrom)
        {
            int i = SFrom - 1;
            while (++i < data.Length)
                if (data[i] == '"')
                {
                    if (i < data.Length - 1 && data[i + 1] == '"')
                    {
                        i++;
                        continue;
                    }
                    else
                        return i;
                }
            return -1;
        }
    }
}
